/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022.
 * Description: add hpvisor dfx
 * Author: lilinjie8 <lilinjie8@huawei.com>
 * Create: 2022-01-24
 */

#ifndef HPVISOR_DFX_H
#define HPVISOR_DFX_H
#include <linux/types.h>
#ifdef CONFIG_ARM64
#include <linux/kvm_host.h>
#endif
#include <linux/sched/clock.h>

int hpvisor_printk(const char *fmt, ...) __attribute__((format(printf, 1, 2)));

#define hpvisor_pr_level(level, fmt, ...)	\
	hpvisor_printk("[%llu][hpvisor][" level "][%s][%s][%i]: " \
	fmt, local_clock(), current->comm, __func__, task_pid_nr(current), ## __VA_ARGS__)

#define hpvisor_err(fmt, ...) hpvisor_pr_level("err", fmt, ## __VA_ARGS__)
#define hpvisor_info(fmt, ...) hpvisor_pr_level("info", fmt, ## __VA_ARGS__)

#ifdef CONFIG_RTOS_HAL_HPVISOR_STAGE2_MEM
#define HPVISOR_MMU_DEBUG_COMMON_BIT BIT(0)
#define HPVISOR_MMU_DEBUG_EACH_PAGE_BIT BIT(1)
extern unsigned int sysctl_hpvisor_mmu_debug;

#define hpvisor_mmu_debug_common(...)	\
	do {	\
		if (sysctl_hpvisor_mmu_debug & HPVISOR_MMU_DEBUG_COMMON_BIT)	\
			hpvisor_info(__VA_ARGS__);	\
	} while (0)
static inline bool hpvisor_mmu_pr_each_page_on(void)
{
	return (sysctl_hpvisor_mmu_debug & HPVISOR_MMU_DEBUG_EACH_PAGE_BIT) != 0;
}
#endif

#ifdef CONFIG_RTOS_HAL_HPVISOR_IRQ_BYPASS
#define HPVISOR_IRQ_DEBUG_COMMON_BIT BIT(0)
#define HPVISOR_IRQ_DEBUG_INJECT_BIT BIT(1)
#define HPVISOR_IRQ_DEBUG_PTIMER_BIT BIT(2)
#define HPVISOR_IRQ_DEBUG_GICR_BIT BIT(3)
#define HPVISOR_IRQ_DEBUG_HVC_BIT BIT(4)

extern unsigned int sysctl_hpvisor_irq_debug;

#define hpvisor_irq_debug_common(...)	\
	do {	\
		if (sysctl_hpvisor_irq_debug & HPVISOR_IRQ_DEBUG_COMMON_BIT)	\
			hpvisor_info(__VA_ARGS__);	\
	} while (0)

#define hpvisor_irq_debug_inject(...)	\
	do {	\
		if (sysctl_hpvisor_irq_debug & HPVISOR_IRQ_DEBUG_INJECT_BIT)	\
			hpvisor_info(__VA_ARGS__);	\
	} while (0)

#define hpvisor_irq_debug_ptimer(...)	\
	do {	\
		if (sysctl_hpvisor_irq_debug & HPVISOR_IRQ_DEBUG_PTIMER_BIT)	\
			hpvisor_info(__VA_ARGS__);	\
	} while (0)

#define hpvisor_irq_debug_gicr(...)	\
	do {	\
		if (sysctl_hpvisor_irq_debug & HPVISOR_IRQ_DEBUG_GICR_BIT)	\
			hpvisor_info(__VA_ARGS__);	\
	} while (0)

#define hpvisor_irq_debug_hvc(...)	\
	do {	\
		if (sysctl_hpvisor_irq_debug & HPVISOR_IRQ_DEBUG_HVC_BIT)	\
					hpvisor_info(__VA_ARGS__);	\
	} while (0)
#endif

#ifdef CONFIG_RTOS_HAL_HPVISOR_VNOTIFY
#define HPVISOR_VNOTIFY_DEBUG_COMMON_BIT BIT(0)
#define HPVISOR_VNOTIFY_DEBUG_SYSCALL_BIT BIT(1)
#define HPVISOR_VNOTIFY_DEBUG_NOTIFY_BIT BIT(2)
#define HPVISOR_VNOTIFY_DEBUG_EXIT_BIT BIT(3)

extern unsigned int sysctl_hpvisor_vnotify_debug;

#define hpvisor_vnotify_debug_common(...)	\
	do {	\
		if (sysctl_hpvisor_vnotify_debug & HPVISOR_VNOTIFY_DEBUG_COMMON_BIT)	\
			hpvisor_info(__VA_ARGS__);	\
	} while (0)

#define hpvisor_vnotify_debug_syscall(...)	\
	do {	\
		if (sysctl_hpvisor_vnotify_debug & HPVISOR_VNOTIFY_DEBUG_SYSCALL_BIT)	\
			hpvisor_info(__VA_ARGS__);	\
	} while (0)

#define hpvisor_vnotify_debug_notify(...)	\
	do {	\
		if (sysctl_hpvisor_vnotify_debug & HPVISOR_VNOTIFY_DEBUG_NOTIFY_BIT)	\
			hpvisor_info(__VA_ARGS__);	\
	} while (0)

#define hpvisor_vnotify_debug_exit(...)	\
	do {	\
		if (sysctl_hpvisor_vnotify_debug & HPVISOR_VNOTIFY_DEBUG_EXIT_BIT)	\
			hpvisor_info(__VA_ARGS__);	\
	} while (0)

#define HPVISOR_VNOTIFY_RECORD_SHIFT 16
#define HPVISOR_VNOTIFY_RECORD_BITS 7
#define HPVISOR_VNOTIFY_RECORD_MASK ((1 << HPVISOR_VNOTIFY_RECORD_BITS) - 1)

static inline int hpvisor_vnotify_get_record_val(void)
{
	return (sysctl_hpvisor_vnotify_debug >> HPVISOR_VNOTIFY_RECORD_SHIFT) & HPVISOR_VNOTIFY_RECORD_MASK;
}

static inline bool hpvisor_vnotify_record_debug_on(void)
{
	return hpvisor_vnotify_get_record_val() > 0;
}
#endif

#ifdef CONFIG_RTOS_HAL_HPVISOR_DFX_ENHANCE
#ifdef CONFIG_ARM64
#include <asm/kvm_host.h>
#include <asm/kvm_emulate.h>
#include <asm/esr.h>
#endif

#define HPVISOR_DFX_COMMON_BIT BIT(0)
#define HPVISOR_DFX_SWITCH_BIT BIT(1)
#define HPVISOR_DFX_RT_BIT BIT(2)
#define REG_LR_IDX 30

extern unsigned int sysctl_hpvisor_dfx_debug;
extern s64 hpvisor_timekeeping_delta_to_ns(s64 delta);

#define hpvisor_dfx_debug_common(...)	\
	do {	\
		if (sysctl_hpvisor_dfx_debug & HPVISOR_DFX_COMMON_BIT)	\
			hpvisor_info(__VA_ARGS__);	\
	} while (0)

static inline bool hpvisor_dfx_rt_debug_on(void)
{
	return (sysctl_hpvisor_dfx_debug & HPVISOR_DFX_RT_BIT) != 0;
}

static inline bool hpvisor_dfx_switch_debug_on(void)
{
	return (sysctl_hpvisor_dfx_debug & HPVISOR_DFX_SWITCH_BIT) != 0;
}

#ifdef CONFIG_ARM64
static inline void hpvisor_dfx_switch_debug(struct kvm_vcpu *vcpu, bool enter)
{
	if (!hpvisor_dfx_switch_debug_on())
		return;

	hpvisor_info("guest %s\n"
		"hcr_el2:0x%llx gic mode:%lu cpu:%d ec:0x%lx pc:0x%llx lr:0x%llx\n"
		"spsr_el1:0x%llx esr_el1:0x%llx sp_el1:0x%llx elr_el1:0x%llx\n"
		"spsr_el2:0x%llx esr_el2:0x%x far_el2:0x%lx mdcr_el2:0x%x %s",
		enter ? "enter" : "exit ", vcpu->arch.hcr_el2,
		vcpu->gic_mode, vcpu->cpu, ESR_ELx_EC(kvm_vcpu_get_esr(vcpu)), vcpu_gp_regs(vcpu)->pc,
		vcpu_gp_regs(vcpu)->regs[30],  __vcpu_sys_reg(vcpu, SPSR_EL1), __vcpu_sys_reg(vcpu, ESR_EL1),
		__vcpu_sys_reg(vcpu, SP_EL1), __vcpu_sys_reg(vcpu, ELR_EL1),
		vcpu_gp_regs(vcpu)->pstate, kvm_vcpu_get_esr(vcpu), kvm_vcpu_get_hfar(vcpu),
		vcpu->arch.mdcr_el2, enter ? "\n\n" : "\n");
}

static inline void hpvisor_dfx_rt_debug(struct kvm_vcpu *vcpu, int last_ec, u64 last_cval)
{
	s64 last_latency;

	last_latency = (s64)(last_cval - vcpu->rt_data.last_vcnt);
	hpvisor_info("########## EXIT_EC(0x%x) ##########\n"
		"last_vcnt:%016llu(cc) last_cval:%016llu(cc) next_cval:%016llu(cc)\n"
		"delta:%016llu(cc) last_latency:%016lld(cc)\n"
		"delta:%016lld(ns) last_latency:%016lld(ns)\n",
		last_ec, vcpu->rt_data.last_vcnt, last_cval,
		read_sysreg(cntv_cval_el0), vcpu->rt_data.delta, last_latency,
		hpvisor_timekeeping_delta_to_ns((s64)(vcpu->rt_data.delta)),
		hpvisor_timekeeping_delta_to_ns(last_latency));
}
#endif
#endif

#ifdef CONFIG_ARM64
extern bool dfx_vcpu_filter(struct kvm_vcpu *vcpu);
extern bool dfx_ec_filter(struct kvm_vcpu *vcpu);
extern void filter_hpvisor_dfx_switch_debug(struct kvm_vcpu *vcpu, bool enter);
extern void set_hpvisor_dfx_hrtimer_debug(unsigned int time_value);
#endif

#endif
